<template>
  <div>
    <p>{{ state.count }}</p>
    <p>{{ shallowData }}</p>
    <p>{{ shallowData.foo }}</p>
    <p>{{ shallowData.nested.bar }}</p>
    <br />
    <p>{{ text }}</p>
    <input type="text" v-model="text">
    <p>counter: {{ counter }}</p>
    <button @click="counter++">counter+</button>
    <button @click="getCurrentScoped">getCurrentScope</button>
    <button @click="scopeStop">scopeStop</button>
  </div>
</template>

<script setup lang="ts">
import { reactive, shallowRef, triggerRef, watchEffect, shallowReactive, isReactive, shallowReadonly, isReadonly, toRaw, markRaw, effectScope, computed, watch, ref, getCurrentScope } from 'vue'
import { useDebouncedRef } from '../utils/useDebouncedRef'

// shallowRef() 是 ref() 的浅层作用形式，和 ref() 不同，浅层ref 的内部之将会原样存储和暴露，并且不会被深层递归地转为响应式，只有.value的访问是响应式。
const state = shallowRef({ count: 1 })
setTimeout(() => {
  // 不会触发视图更改
  // state.value.count = 2
  // 会触发视图更改
  state.value = { count: 3 }
}, 2000)
console.log(state)

console.log('============')

// triggerRef() 强制触发依赖于一个浅层ref的副作用，这通常在对浅引用的内部值进行深度变更后使用

const shallow = shallowRef({
  greet: 'Hello, World'
})

// 触发该副作用第一次应该会打印 Hello, World
watchEffect(() => {
  console.log(shallow.value.greet)
})

// 这次变更不应触发副作用，因为这个 ref 是浅层的
setTimeout(() => {
  shallow.value.greet = 'Hello, universe'
  // 打印 Hello, universe
  triggerRef(shallow)
}, 3000)

console.log('============')

// shallowReactive()是reactive()的浅层作用形式。和reactive()不同，这里没有深层级的转换：一个浅层响应式对象里只有根级别的property是响应式的，property的值会被原样存储和暴露，这也意味着值为ref的property不会被自动解包了。
const shallowData = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})


console.log(shallowData)
setTimeout(() => {
  // 更改状态自身的属性是响应式的
  shallowData.foo++
  // 不是响应式的
  shallowData.nested.bar++
}, 2000)
// 不是响应式的
console.log(isReactive(shallowData.nested)) // false

// customRef
const text = useDebouncedRef('hello', 2000)

console.log('==============')

// shallowReadonly()是 readonly()的浅层作用形式。和readonly() 不同，这里没有深层级的转换：只有根层级的 property 变为了只读。property的值都会被原样存储和暴露，这也意味着值为ref的property不会被自动解包了。
const shaReadonly = shallowReadonly({
  a: 1,
  nested: {
    b: 2
  }
})
// shaReadonly.a++ // 更改状态自身的属性会失败
shaReadonly.nested.b++ // 但可以更改下层嵌套对象
console.log(isReadonly(shaReadonly.nested)) // false

console.log('===========')

// toRaw() 根据一个Vue创建的代理返回其原始对象
// toRaw() 可以返回由 reactive()，readonly()，shallowReactive()，shallowReadonly() 创建的代理对应的原始对象。这是一个可以用于临时读取而不引起代理访问/跟踪开销，或是写入而不触发更改的特殊方法，不建议保存对原始对象的持久引用，谨慎使用。

const raw = {}
const reactiveRaw = reactive(raw)
console.log(toRaw(reactiveRaw) === raw) // true


console.log('===========')
// markRaw() 将一个对象标记为不可被转为代理，返回该对象本身

const mark = markRaw({})
console.log(isReactive(reactive(mark))) // false

// 也适用于嵌套在其他响应性对象
const rawBar = reactive({ mark })
console.log(isReactive(rawBar.mark)) // false

const a = markRaw({
  nested: {}
})
const b = reactive({
  // 尽管 a 被标记为原始对象，但 a.nested 却没有
  nested: a.nested
})
console.log(a.nested === b.nested) // false

console.log('===========')

// effectScope() 创建一个 effect 作用域，可以捕获其中所创建的响应式副作用（即计算属性和侦听器），这样捕获到的副作用可以一起处理。
const counter = ref(1)
const scope = effectScope()
scope.run(() => {
  const doubled = computed(() => counter.value * 2)

  watch(doubled, () => {
    console.log(doubled.value)
    getCurrentScoped()
  })

  watchEffect(() => console.log('counter', doubled.value))
})

const getCurrentScoped = () => {
  console.log(getCurrentScope()) // 这个为undefined
}

const scopeStop = () => {
  // 处理掉当前作用域内的所有effect
  scope.stop()
}
</script>

<style scoped>

</style>